package com.gitee.qdbp.general.system.web.controller.personnel;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.result.IResultMessage;
import com.gitee.qdbp.able.result.ResponseMessage;
import com.gitee.qdbp.able.result.ResultCode;
import com.gitee.qdbp.base.annotation.OperateRecord;
import com.gitee.qdbp.base.enums.OperateType;
import com.gitee.qdbp.base.model.consts.Keys;
import com.gitee.qdbp.base.shiro.token.AuthedLoginToken;
import com.gitee.qdbp.base.system.model.IAccount;
import com.gitee.qdbp.base.system.model.IAccountType;
import com.gitee.qdbp.base.system.service.IDefaultValueConfig;
import com.gitee.qdbp.base.utils.SessionTools;
import com.gitee.qdbp.base.utils.WebTools;
import com.gitee.qdbp.general.system.api.personnel.model.AccountBean;
import com.gitee.qdbp.general.system.api.personnel.model.UserCoreBean;
import com.gitee.qdbp.general.system.api.personnel.model.UserCoreUpdate;
import com.gitee.qdbp.general.system.api.personnel.model.UserCoreWhere;
import com.gitee.qdbp.general.system.api.personnel.service.IUserAuthExecutor;
import com.gitee.qdbp.general.system.api.personnel.service.IUserCoreExecutor;
import com.gitee.qdbp.general.system.api.personnel.service.IUserCoreQueryer;
import com.gitee.qdbp.general.system.error.UserErrorCode;
import com.gitee.qdbp.general.system.web.shiro.simple.LoginUser;
import com.gitee.qdbp.tools.utils.ConvertTools;
import com.gitee.qdbp.tools.utils.RandomTools;
import com.gitee.qdbp.tools.utils.StringTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 用户注册登录改密码<br>
 * <br>
 * 找回密码, 三步操作: <br>
 * 1. sendRepasswordValidCode: 根据手机号码/邮箱发送短信/邮件验证码<br>
 * 2. createRepasswordToken: 根据短信或邮件验证码创建token<br>
 * 3. repasswordByToken: 根据token设置新的密码<br>
 * 找回密码, 二步操作: <br>
 * 1. sendRepasswordValidCode: 根据手机号码/邮箱发送短信/邮件验证码<br>
 * 2. repasswordByValidCode: 根据短信或邮件验证码设置新的密码<br>
 * <br>
 * 重置手机号码, 三步操作: <br>
 * 1. createChangePhoneToken: 先根据[原手机号码+登录密码]创建token<br>
 * 2. sendChangePhoneValidCode: 根据token和新手机号码发送短信验证码<br>
 * 3. changePhoneByValidCode: 根据短信验证码设置新手机号码<br>
 *
 * @author zhaohuihua
 * @version 150915
 */
public class BaseAuthController {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /** 短信/邮件验证码登录时当前SESSION已验证过的账号 **/
    protected static String LOGIN_VALIDATED_ACCOUNT = "LOGIN_VALIDATED_ACCOUNT";
    /** 短信/邮件验证码登录时当前SESSION已验证过的用户对象 **/
    protected static String LOGIN_VALIDATED_USER = "LOGIN_VALIDATED_USER";
    /** 短信/邮件验证码注册时当前SESSION已验证过的账号 **/
    protected static String REGISTER_VALIDATED_ACCOUNT = "REGISTER_VALIDATED_ACCOUNT";
    /** 找回密码时生成的凭证 **/
    protected static String REPASSWORD_VALIDATED_TOKEN = "REPASSWORD_VALIDATED_TOKEN";
    /** 找回密码时当前SESSION已验证过的账号 **/
    protected static String REPASSWORD_VALIDATED_ACCOUNT = "REPASSWORD_VALIDATED_ACCOUNT";
    /** 修改手机号码时生成的凭证 **/
    protected static String CHANGE_PHONE_VALIDATED_TOKEN = "CHANGE_PHONE_VALIDATED_TOKEN";
    /** 修改手机号码时当前SESSION已验证过的账号 **/
    protected static String CHANGE_PHONE_VALIDATED_ACCOUNT = "CHANGE_PHONE_VALIDATED_ACCOUNT";

    @Autowired
    protected IUserAuthExecutor userAuthExecutor;
    @Autowired
    protected IUserCoreExecutor userCoreExecutor;
    @Autowired
    protected IUserCoreQueryer userCoreQueryer;
    @Autowired
    protected IDefaultValueConfig defaultValues;

    protected IAccount encrypt(IAccount account) {
        if (account instanceof UserCoreBean.Wrap) {
            UserCoreBean original = ((UserCoreBean.Wrap) account).original();
            UserCoreBean copied = original.to(UserCoreBean.class);
            copied.setPhone(StringTools.hidden(copied.getPhone()));
            copied.setEmail(StringTools.hidden(copied.getEmail()));
            return new LoginUser(copied);
        } else {
            return account;
        }
    }

    /** 查询当前登录用户 **/
    @ResponseBody
    @RequestMapping("me")
    @OperateRecord(enable = false)
    public ResponseMessage me() {
        try {
            IAccount account = SessionTools.getLoginUser();
            return new ResponseMessage(encrypt(account));
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 当前登录用户修改自己的基本信息 **/
    @ResponseBody
    @RequestMapping("base-info/update")
    @OperateRecord(value = "用户基本信息:修改", type = OperateType.UPDATE)
    public ResponseMessage updateBaseInfo(UserCoreUpdate model) {

        // 只允许修改这些内容
        UserCoreUpdate ud = new UserCoreUpdate();
        copyUserBaseInfo(model, ud);

        try {
            IAccount me = SessionTools.getLoginUser();
            ud.getWhere(true).setId(me.getId());

            userCoreExecutor.update(ud, me);

            // 查询修改后的内容
            UserCoreBean newer = userCoreQueryer.find(me.getId());
            // 修改Session中的内容
            UserCoreBean older = ((UserCoreBean.Wrap) me).original();
            copyUserBaseInfo(newer, older);

            // 刷新分布式Session缓存中的数据
            SessionTools.sessionChanged();
            return new ResponseMessage(encrypt(me));
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    protected void copyUserBaseInfo(UserCoreBean source, UserCoreBean target) {
        // target.setUserName(source.getUserName()); // 登录用户名
        target.setNickName(source.getNickName()); // 昵称
        target.setRealName(source.getRealName()); // 真实姓名
        // target.setPhone(source.getPhone()); // 电话
        // target.setEmail(source.getEmail()); // 邮箱
        target.setGender(source.getGender()); // 性别(0.未知|1.男|2.女)
        target.setPhoto(source.getPhoto()); // 头像
        target.setCity(source.getCity()); // 城市
        target.setIdentity(source.getIdentity()); // 身份证
    }

    /**
     * 检查手机/邮箱是否已经存在: 用于注册<br>
     * account: 手机号码/邮箱
     * 
     * @param model 参数
     * @return code=SUCCESS, 检查通过(即不存在); code=其他值表示检查未通过, message=失败原因
     */
    @ResponseBody
    @RequestMapping("register/available")
    @OperateRecord(value = "检查账号是否已经存在", type = OperateType.QUERY)
    public ResponseMessage availableForRegister(AccountBean model) {
        String msg = "Failed to check account. ";
        if (VerifyTools.isBlank(model.getAccount())) {
            log.error(msg + "params is null: account");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            String account = model.getAccount();
            if (VerifyTools.isBlank(account)) {
                return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
            }
            if (VerifyTools.isBlank(model.getType())) {
                model.setType(defaultValues.getUserType());
            }

            UserCoreWhere where = new UserCoreWhere();
            where.setTenantCode(model.getFactory());
            where.setUserType(model.getType());
            if (StringTools.isPhone(account)) {
                where.setPhone(account);
            } else if (StringTools.isEmail(account)) {
                where.setEmail(account);
            } else {
                log.error(msg + "params format error: account={}", account);
                return new ResponseMessage(ResultCode.PARAMETER_FORMAT_ERROR);
            }
            UserCoreBean bean = userCoreQueryer.find(where);
            if (bean != null) {
                log.warn(msg + "account has registered: account={}", account);
                if (StringTools.isPhone(account)) {
                    return new ResponseMessage(UserErrorCode.PHONE_HAS_REGISTER);
                } else if (StringTools.isEmail(account)) {
                    return new ResponseMessage(UserErrorCode.EMAIL_HAS_REGISTER);
                } else {
                    return new ResponseMessage(UserErrorCode.USER_HAS_REGISTER);
                }
            } else {
                return new ResponseMessage();
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /**
     * 检查手机/邮箱是否为激活用户: 用于登录或找回密码<br>
     * account: 手机号码/邮箱
     * 
     * @param model 参数
     * @return code=SUCCESS, 检查通过(即存在); code=其他值表示检查未通过, message=失败原因
     */
    @ResponseBody
    @RequestMapping("login/available")
    @OperateRecord(value = "检查账号是否为激活用户", type = OperateType.QUERY)
    public ResponseMessage availableForLogin(AccountBean model) {
        String msg = "Failed to check account. ";
        if (VerifyTools.isBlank(model.getAccount())) {
            log.error(msg + "params is null: account");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            String account = model.getAccount();
            if (VerifyTools.isBlank(account)) {
                return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
            }
            if (VerifyTools.isBlank(model.getType())) {
                model.setType(defaultValues.getUserType());
            }

            IAccountType[] choose = ConvertTools.toArray(defaultValues.getUserTypeChoose(), IAccountType.class);
            UserCoreBean bean = userAuthExecutor.findActivityUser(model, choose);

            if (bean == null) {
                log.warn(msg + "account not exist: account={}", account);
                return new ResponseMessage(UserErrorCode.USER_NOT_EXIST);
            } else {
                return new ResponseMessage();
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 发送登录短信/邮件验证码, account=手机号码/邮箱地址 **/
    @ResponseBody
    @RequestMapping("login/send-valid-code")
    @OperateRecord("登录:发送验证码")
    public ResponseMessage sendLoginValidCode(AccountBean model) {
        // 发送登录短信/邮件验证码
        // 只有先找到用户, 才能发短信, 因此发送完短信就确定了用户
        // 此时将用户信息缓存在SESSION中
        // 之后只需要在提交短信/邮件验证码时, 验证一下短信是否正确即可
        try {
            String account = model.getAccount();
            if (VerifyTools.isBlank(account)) {
                return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
            }
            if (VerifyTools.isBlank(model.getType())) {
                model.setType(defaultValues.getUserType());
            }

            IAccountType[] choose = ConvertTools.toArray(defaultValues.getUserTypeChoose(), IAccountType.class);
            UserCoreBean user = userAuthExecutor.sendLoginValidCode(model, choose);
            model.setType(user.getUserType()); // 账号类型已明确
            SessionTools.setAttribute(LOGIN_VALIDATED_ACCOUNT, model);
            SessionTools.setAttribute(LOGIN_VALIDATED_USER, user);
            return new ResponseMessage();
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 检查登录短信/邮件验证码, validCode=短信/邮件验证码 **/
    @ResponseBody
    @RequestMapping("login/check-valid-code")
    @OperateRecord(value = "登录:检查验证码", type = OperateType.QUERY)
    public ResponseMessage checkLoginValidCode(String validCode) {
        String msg = "Failed to check LoginValidCode. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            AccountBean bean = SessionTools.getAttribute(LOGIN_VALIDATED_ACCOUNT);
            if (bean == null) {
                log.error(msg + "validated account not found in session!");
                return new ResponseMessage(UserErrorCode.USER_LOGIN_VALID_CODE_EXPIRED);
            } else {
                userAuthExecutor.checkLoginValidCode(bean, validCode, false);
                return new ResponseMessage();
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 以短信/邮件验证码方式登录, validCode=短信/邮件验证码 **/
    @ResponseBody
    @RequestMapping("login/by-valid-code")
    @OperateRecord("登录:根据验证码登录")
    public ResponseMessage loginValidCode(String validCode, Boolean rememberMe, HttpServletRequest request) {
        if (rememberMe == null) rememberMe = false;
        String msg = "Failed to login by phone. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        UserCoreBean user = SessionTools.getAttribute(LOGIN_VALIDATED_USER);
        if (user == null) {
            log.error(msg + "validated user not found in session!");
            return new ResponseMessage(UserErrorCode.USER_LOGIN_VALID_CODE_EXPIRED);
        }
        AccountBean bean = SessionTools.getAttribute(LOGIN_VALIDATED_ACCOUNT);
        if (bean == null) {
            log.error(msg + "validated account not found in session!");
            return new ResponseMessage(UserErrorCode.USER_LOGIN_VALID_CODE_EXPIRED);
        }

        try { // 检查登录短信/邮件验证码
            userAuthExecutor.checkLoginValidCode(bean, validCode, true);
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
        // 操作成功, 清空SESSION缓存
        SessionTools.removeAttribute(LOGIN_VALIDATED_USER);
        SessionTools.removeAttribute(LOGIN_VALIDATED_ACCOUNT);

        try { // 执行登录
            IAccount account = new LoginUser(user);
            String host = new WebTools(request).getIpAddress();
            AuthedLoginToken token = new AuthedLoginToken(account, rememberMe, host);
            SecurityUtils.getSubject().login(token);
            return new ResponseMessage(user);
        } catch (AuthenticationException e) {
            if (e.getCause() instanceof ServiceException) {
                return new ResponseMessage((ServiceException) e.getCause());
            } else {
                log.error("Failed to exec authentication login by valid code, unknown exception.", e);
                return new ResponseMessage(UserErrorCode.USERINFO_LOGIN_ERROR);
            }
        }
    }

    /** 发送注册短信/邮件验证码, account=手机号码/邮箱地址 **/
    @ResponseBody
    @RequestMapping("register/send-valid-code")
    @OperateRecord("注册:发送验证码")
    public ResponseMessage sendRegisterValidCode(AccountBean model) {
        String msg = "Failed to send RegisterValidCode. ";
        if (VerifyTools.isBlank(model.getAccount())) {
            log.error(msg + "params is null: account");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        if (VerifyTools.isBlank(model.getType())) {
            model.setType(defaultValues.getUserType());
        }

        try {
            userAuthExecutor.sendRegisterValidCode(model);
            SessionTools.setAttribute(REGISTER_VALIDATED_ACCOUNT, model);
            return new ResponseMessage();
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 检查注册短信/邮件验证码, code=短信/邮件验证码 **/
    @ResponseBody
    @RequestMapping("register/check-valid-code")
    @OperateRecord(value = "注册:检查验证码", type = OperateType.QUERY)
    public ResponseMessage checkRegisterValidCode(String validCode) {
        String msg = "Failed to check RegisterValidCode. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            AccountBean bean = SessionTools.getAttribute(REGISTER_VALIDATED_ACCOUNT);
            if (bean == null) {
                log.error(msg + "validated account not found in session!");
                return new ResponseMessage(UserErrorCode.USER_REGISTER_VALID_CODE_EXPIRED);
            } else {
                userAuthExecutor.checkRegisterValidCode(bean, validCode);
                return new ResponseMessage();
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /**
     * 根据手机号码注册<br>
     * validCode: 短信/邮件验证码(必填)<br>
     * password: 密码(必填)<br>
     * nickName: 昵称<br>
     * realName: 真实姓名<br>
     * gender: 性别(MALE:男, FEMALE:女)<br>
     * photo: 头像URL<br>
     * 
     * @param model 参数
     * @param validCode 短信/邮件验证码
     * @param login 是否自动登录
     * @param rememberMe 自动登录时是否记住密码
     * @return 响应消息, 根据code=SUCCESS判断是否成功
     */
    @ResponseBody
    @RequestMapping("register/by-valid-code")
    @OperateRecord("注册:提交")
    public ResponseMessage registerByValidCode(UserCoreBean model, String validCode, Boolean login, Boolean rememberMe,
            HttpServletRequest request) {
        if (login == null) login = false;
        if (rememberMe == null) rememberMe = false;

        String msg = "Failed to register by valid code. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        try {
            AccountBean bean = SessionTools.getAttribute(REGISTER_VALIDATED_ACCOUNT);
            if (bean == null) {
                log.error(msg + "validated account not found in session!");
                return new ResponseMessage(UserErrorCode.USER_REGISTER_VALID_CODE_EXPIRED);
            } else {
                { // 这几个参数是之前发验证码时确认过的
                    model.setTenantCode(bean.getFactory());
                    model.setUserType(bean.getType());
                    String account = bean.getAccount();
                    if (StringTools.isPhone(account)) {
                        model.setPhone(account);
                    } else if (StringTools.isEmail(account)) {
                        model.setEmail(account);
                    } else {
                        model.setUserCode(account); // 登录账号/工号
                    }
                }
                // deptCode
                if (VerifyTools.isBlank(model.getDeptCode())) {
                    model.setDeptCode(Keys.ROOT_CODE);
                }
                UserCoreBean user = userAuthExecutor.registerByValidCode(model, validCode);
                SessionTools.removeAttribute(REGISTER_VALIDATED_ACCOUNT); // 操作成功, 清空SESSION缓存

                ResponseMessage resp = new ResponseMessage(user);
                if (login) { // 自动登录
                    IResultMessage loginResult;
                    try {
                        IAccount account = new LoginUser(user);
                        String host = new WebTools(request).getIpAddress();
                        AuthedLoginToken token = new AuthedLoginToken(account, rememberMe, host);
                        token.setRememberMe(rememberMe);
                        SecurityUtils.getSubject().login(token);
                        loginResult = ResultCode.SUCCESS;
                    } catch (Exception e) {
                        if (e.getCause() instanceof ServiceException) {
                            loginResult = (ServiceException) e.getCause();
                        } else {
                            loginResult = ResultCode.SERVER_INNER_ERROR;
                        }
                        log.warn("Register success, but login failed, {}, {}", bean, loginResult, e);
                    }
                    resp.addExtra("login", loginResult);
                }
                return resp;
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 修改自己的密码 **/
    @ResponseBody
    @RequestMapping("password/update")
    // @RequiresPermissions("personnel:mine:password")
    @OperateRecord("修改密码")
    public ResponseMessage updatePassword(String oldPassword, String newPassword) {
        String msg = "Failed to update password. ";
        if (VerifyTools.isBlank(oldPassword)) {
            log.error(msg + "params is null: oldPassword");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        if (VerifyTools.isBlank(newPassword)) {
            log.error(msg + "params is null: newPassword");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            IAccount user = SessionTools.getLoginUser();
            userAuthExecutor.updatePassword(user.getId(), oldPassword, newPassword);
            return new ResponseMessage();
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 发送找回密码短信/邮件验证码, account=手机号码/邮箱地址 **/
    @ResponseBody
    @RequestMapping("repassword/send-valid-code")
    // @RequiresPermissions("personnel:mine:repassword")
    @OperateRecord("找回密码:发送验证码")
    public ResponseMessage sendRepasswordValidCode(AccountBean model) {
        String msg = "Failed to send RepasswordValidCode. ";
        if (VerifyTools.isBlank(model.getAccount())) {
            log.error(msg + "params is null: account");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            if (VerifyTools.isBlank(model.getType())) {
                model.setType(defaultValues.getUserType());
            }
            IAccountType[] choose = ConvertTools.toArray(defaultValues.getUserTypeChoose(), IAccountType.class);
            UserCoreBean user = userAuthExecutor.sendRepasswordValidCode(model, choose);
            model.setType(user.getUserType()); // 账号类型已明确
            SessionTools.setAttribute(REPASSWORD_VALIDATED_ACCOUNT, model);
            return new ResponseMessage();
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /** 检查找回密码短信/邮件验证码, validCode=短信/邮件验证码 **/
    @ResponseBody
    @RequestMapping("repassword/check-valid-code")
    // @RequiresPermissions("personnel:mine:repassword")
    @OperateRecord(value = "找回密码:检查验证码", type = OperateType.QUERY)
    public ResponseMessage checkRepasswordValidCode(String validCode) {
        String msg = "Failed to check RepasswordValidCode. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            AccountBean bean = SessionTools.getAttribute(REPASSWORD_VALIDATED_ACCOUNT);
            if (bean == null) {
                log.error(msg + "validated account not found in session!");
                return new ResponseMessage(UserErrorCode.USER_REPASSWORD_VALID_CODE_EXPIRED);
            } else {
                userAuthExecutor.checkRepasswordValidCode(bean, validCode, false);
                return new ResponseMessage();
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /**
     * 找回密码三步操作的第二步: 检查验证码并创建token
     * 
     * @param validCode 短信/邮件验证码或邮件验证码
     * @return token
     */
    @ResponseBody
    @RequestMapping("repassword/create-token")
    // @RequiresPermissions("personnel:mine:repassword")
    @OperateRecord("找回密码:检查验证码")
    public ResponseMessage createRepasswordToken(String validCode) {
        String msg = "Failed to create RepasswordToken. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        AccountBean bean = SessionTools.getAttribute(REPASSWORD_VALIDATED_ACCOUNT);
        if (bean == null) {
            log.error(msg + "validated account not found in session!");
            return new ResponseMessage(UserErrorCode.USER_REPASSWORD_VALID_CODE_EXPIRED);
        } else {
            // 检查验证码
            userAuthExecutor.checkRepasswordValidCode(bean, validCode, false);

            // 生成token并保存到session中
            String token = RandomTools.generateUuid();
            SessionTools.setAttribute(REPASSWORD_VALIDATED_TOKEN, token);

            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
            return new ResponseMessage(map);
        }
    }

    /**
     * 找回密码三步操作的第三步: 检查token并修改密码
     * 
     * @param token 第二步生成的token
     * @param newPassword 新的密码
     * @return
     */
    @ResponseBody
    @RequestMapping("repassword/by-token")
    // @RequiresPermissions("personnel:mine:repassword")
    @OperateRecord("找回密码:修改密码")
    public ResponseMessage repasswordByToken(String token, String newPassword) {
        String msg = "Failed to execute repassword. ";
        if (VerifyTools.isBlank(token)) {
            log.error(msg + "params is null: token");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        try {
            String cacheToken = SessionTools.getAttribute(REPASSWORD_VALIDATED_TOKEN);
            if (cacheToken == null) {
                log.error(msg + "token not found in session!");
                return new ResponseMessage(ResultCode.OPERATE_TIMEOUT);
            }
            if (!cacheToken.equalsIgnoreCase(token)) {
                log.error(msg + "token not matches in session! token={}, cacheToken={}", token, cacheToken);
                // 由于token是隐藏提交的, 正常流程token不可能错误, 如果出现错误说明是恶意用户的攻击尝试
                return new ResponseMessage(ResultCode.OPERATE_TIMEOUT);
            }

            AccountBean bean = SessionTools.getAttribute(REPASSWORD_VALIDATED_ACCOUNT);
            if (bean == null) {
                log.error(msg + "validated account not found in session!");
                return new ResponseMessage(UserErrorCode.USER_REPASSWORD_VALID_CODE_EXPIRED);
            } else {
                userAuthExecutor.updatePassword(bean, newPassword);
                // 操作成功, 清空SESSION缓存
                SessionTools.removeAttribute(REPASSWORD_VALIDATED_TOKEN);
                SessionTools.removeAttribute(REPASSWORD_VALIDATED_ACCOUNT);
                return new ResponseMessage();
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /**
     * 找回密码二步操作的第二步: 根据短信或邮件验证码设置新的密码
     * 
     * @param validCode 短信/邮件验证码或邮件验证码
     * @param newPassword 新的密码
     * @return
     */
    @ResponseBody
    @RequestMapping("repassword/by-valid-code")
    // @RequiresPermissions("personnel:mine:repassword")
    @OperateRecord("找回密码:修改密码")
    public ResponseMessage repasswordByValidCode(String validCode, String newPassword) {
        String msg = "Failed to create RepasswordToken. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: code");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        AccountBean bean = SessionTools.getAttribute(REPASSWORD_VALIDATED_ACCOUNT);
        if (bean == null) {
            log.error(msg + "validated account not found in session!");
            return new ResponseMessage(UserErrorCode.USER_REPASSWORD_VALID_CODE_EXPIRED);
        } else {
            try {
                // 检查验证码
                userAuthExecutor.checkRepasswordValidCode(bean, validCode, false);
                // 修改密码
                userAuthExecutor.updatePassword(bean, newPassword);
                // 操作成功, 清空SESSION缓存
                SessionTools.removeAttribute(REPASSWORD_VALIDATED_TOKEN);
                SessionTools.removeAttribute(REPASSWORD_VALIDATED_ACCOUNT);
                return new ResponseMessage();
            } catch (ServiceException e) {
                return new ResponseMessage(e);
            }
        }
    }

    /**
     * 重置手机号码三步操作的第一步: 创建token
     * 
     * @param password 登录密码
     * @param phonePartCode 手机号码中隐藏的4位数字, 当用户首次设置手机号码时可为空
     * @return
     */
    @ResponseBody
    @RequestMapping("change-phone/create-token")
    // @RequiresPermissions("personnel:mine:change-phone")
    @OperateRecord("更换手机号码:验证用户信息")
    public ResponseMessage createChangePhoneToken(String password, String phonePartCode) {
        String msg = "Failed to create ChangePhoneToken. ";
        if (VerifyTools.isBlank(password)) {
            log.error(msg + "params is null: password");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        try {
            IAccount user = SessionTools.getLoginUser();
            // 如果用户之前设置过手机号码, 验证补全数字phonePartCode是否正确
            String phone = user.getPhone();
            if (VerifyTools.isNotBlank(phone)) {
                if (VerifyTools.isBlank(phonePartCode)) {
                    log.error(msg + "params is null: phoneCode");
                    return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
                }
                String encrypted = StringTools.hidden(phone);
                String temp;
                if (phonePartCode.length() == phone.length()) {
                    temp = phonePartCode;
                } else {
                    temp = Pattern.compile("\\*+").matcher(encrypted).replaceAll(phonePartCode);
                }
                if (!phone.equalsIgnoreCase(temp)) {
                    log.error(msg + "phoneCode not matches: phoneCode={} -- {}({})", phonePartCode, phone, encrypted);
                    return new ResponseMessage(UserErrorCode.PHONE_PART_CODE_NOT_MATCHES);
                }
            }
            // 验证用户的登录密码是否正确
            userAuthExecutor.checkPassword(user.getId(), password);

            // 生成token并保存到session中
            String token = RandomTools.generateUuid();
            SessionTools.setAttribute(CHANGE_PHONE_VALIDATED_TOKEN, token);

            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
            return new ResponseMessage(map);
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    /**
     * 重置手机号码三步操作的第二步: 向新手机发送验证码
     * 
     * @param phone 新手机号码
     * @param token 第一步生成的token
     * @return
     */
    @ResponseBody
    @RequestMapping("change-phone/send-valid-code")
    // @RequiresPermissions("personnel:mine:change-phone")
    @OperateRecord(value = "更换手机号码:发送验证码", type = OperateType.QUERY)
    public ResponseMessage sendChangePhoneValidCode(String phone, String token) {
        String msg = "Failed to send ChangePhoneValidCode. ";
        if (VerifyTools.isBlank(phone)) {
            log.error(msg + "params is null: phone");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        if (VerifyTools.isBlank(token)) {
            log.error(msg + "params is null: token");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }

        try {
            String cacheToken = SessionTools.getAttribute(CHANGE_PHONE_VALIDATED_TOKEN);
            if (cacheToken == null) {
                log.error(msg + "token not found in session!");
                return new ResponseMessage(ResultCode.OPERATE_TIMEOUT);
            }
            if (!cacheToken.equalsIgnoreCase(token)) {
                log.error(msg + "token not matches in session! token={}, cacheToken={}", token, cacheToken);
                // 由于token是隐藏提交的, 正常流程token不可能错误, 如果出现错误说明是恶意用户的攻击尝试
                return new ResponseMessage(ResultCode.OPERATE_TIMEOUT);
            }

            IAccount user = SessionTools.getLoginUser();
            userAuthExecutor.sendChangePhoneValidCode(user.getId(), phone);
            SessionTools.setAttribute(CHANGE_PHONE_VALIDATED_ACCOUNT, phone);
            // 发送验证码可以重发, 因此不清空token
            // session.removeAttribute(CHANGE_PHONE_VALIDATED_TOKEN);
            return new ResponseMessage();
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

    // /** 检查更换手机号码的短信/邮件验证码 **/
    // @ResponseBody
    // @RequestMapping("change-phone/check-valid-code")
    // // @RequiresPermissions("personnel:mine:change-phone")
    // @OperateRecord("更换手机号码:检查验证码")
    // public ResponseMessage checkChangePhoneValidCode(String validCode) {
    //     String msg = "Failed to check ChangePhoneValidCode. ";
    //     if (VerifyTools.isBlank(validCode)) {
    //         log.error(msg + "params is null: validCode");
    //         return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
    //     }
    //    
    //     try {
    //         IAccount user = SessionTools.getLoginUser();
    //         String phone = SessionTools.getAttribute(CHANGE_PHONE_VALIDATED_ACCOUNT);
    //         SessionTools.setAttribute(CHANGE_PHONE_VALIDATED_ACCOUNT, phone);
    //         if (phone == null) {
    //             log.error(msg + "validated account not found in session!");
    //             return new ResponseMessage(UserErrorCode.CHANGE_PHONE_VALID_CODE_EXPIRED);
    //         } else {
    //             userAuthExecutor.checkChangePhoneValidCode(user.getId(), phone, validCode);
    //             return new ResponseMessage();
    //         }
    //     } catch (ServiceException e) {
    //         return new ResponseMessage(e);
    //     }
    // }

    /** 重置手机号码三步操作的第三步: 根据短信验证码重置手机号码 **/
    @ResponseBody
    @RequestMapping("change-phone/by-valid-code")
    // @RequiresPermissions("personnel:mine:change-phone")
    @OperateRecord("更换手机号码:重置手机号码")
    public ResponseMessage changePhoneByValidCode(String validCode) {
        String msg = "Failed to execute change phone. ";
        if (VerifyTools.isBlank(validCode)) {
            log.error(msg + "params is null: validCode");
            return new ResponseMessage(ResultCode.PARAMETER_IS_REQUIRED);
        }
        try {
            IAccount user = SessionTools.getLoginUser();
            String phone = SessionTools.getAttribute(CHANGE_PHONE_VALIDATED_ACCOUNT);
            SessionTools.setAttribute(CHANGE_PHONE_VALIDATED_ACCOUNT, phone);
            if (phone == null) {
                log.error(msg + "validated account not found in session!");
                return new ResponseMessage(UserErrorCode.CHANGE_PHONE_VALID_CODE_EXPIRED);
            } else {
                userAuthExecutor.changePhoneByValidCode(user.getId(), phone, validCode);
                // 操作成功, 清空SESSION缓存
                SessionTools.removeAttribute(CHANGE_PHONE_VALIDATED_TOKEN);
                SessionTools.removeAttribute(CHANGE_PHONE_VALIDATED_ACCOUNT);
                ((LoginUser) user).original().setPhone(phone);
                SessionTools.sessionChanged();
                return new ResponseMessage(encrypt(user));
            }
        } catch (ServiceException e) {
            return new ResponseMessage(e);
        }
    }

}
